<?php
/*
 * emarket - a website to create your own online-shop.  
 * Copyright (C) 2009  Frédéric Delorme
 *
 * This file is part of emarket.
 *
 * emarket is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * emarket is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with emarket.  If not, see <http://www.gnu.org/licenses/>.
 *
 * To contact me: frederic.delorme@gmail.com
 * to see some information on te project : http://code.google.col/p/os-emarket/
 *
 */
global $framework;
require_once("webapp/application/application.php");
require_once($framework."/persistence/data.php");
require_once($framework."/debug.php");
require_once($framework."/i18n.php");
require_once($framework."/template.php");

class ModelManager extends Application{
	/**
	 * INstance
	 * @var Model
	 */
	static protected $_instance = null;
	/**
	 * Data persistence
	 * @var Data
	 */
	private $data	= "";
	/**
	 * array containing model.ini file.
	 * @var array
	 */
	private $model	= "";
	/**
	 * List of prperties about tables of th model.
	 * @var array
	 */
	private $tables = null;
	/**
	 * Schema description
	 * @var array
	 */
	private $schema = null;
	
	/**
	 * Default constructor fo the Model enhancer.
	 * @return unknown_type
	 */
	public function __construct(){
		$this->data = Data::getInstance();
		$this->model = parse_ini_file("configuration/model/schema.ini",true);
		$this->tables = array();
		$this->schema = array();
	}
	
	/**
	 * Extract all needed info from definition and format it to usable infos array.
	 * @param $format
	 * @return unknown_type
	 */
	private function extractFieldInfos($table,$field,$format=""){
		$infos = array();
		if(strstr($format,'@')!=""){
			//__debug("$field =>foreignkey");
			$fk="".substr($format,1,strlen($format)-1);
			if(empty($table['foreignkey'])){
				$table['foreignkey']=array();
			}
			if(empty($infos['foreignkey'])){
				$infos['foreignkey']=array();
			}
			$fkdetail=split('[./-]',$fk);
			//__debug("fk:".print_r($fkdetail,true));
			$infos['foreignkey'][$field]=$fk;
			$table['foreignkey'][$field]=array('table'=>$fkdetail[0],'field'=>$fkdetail[1]);
		}else{
			$details= split(",",$format);
			//type and size
			$typeformat = split('[|]',$details[0]);
			$possize = strpos($typeformat[0],"(");
			if($possize>0){
				$type=substr($typeformat[0],0,$possize);
				$size=substr($typeformat[0],$possize+1,strlen($typeformat[0])-$possize-2);
				
				$infos['type'] = $type;
				$infos['size'] = $size;
			}else{
				$infos['type'] = $typeformat[0];
			}
			//format
			if(!empty($typeformat[1])){
				$infos['format']= substr($typeformat[1],1,strlen($typeformat[1])-2);
			}
			if(!empty($typeformat[2])){
				$infos['default'] = $typeformat[2];
			}
			// other information
			if(count($details)>1){
				for($i=1;$i<count($details);$i++){
					//__debug("detail: $details[$i]");
					switch($details[$i]){
						case "primarykey":
							if(empty($table['primarykey'])){
								$table['primarykey']=array();
							}
							$table['primarykey'][]=$field;
							$infos['attribute']="primarykey";
					}
					$infos['options'][$i]=$details[$i];
				}
			}
		}
		return $infos;
	}
	
	/**
	 * Extract model.ini data to schema
	 */
	private function unserialize(){
		$log="";
		foreach($this->model as $tablename=>$structure ){
			$this->tables[$tablename]=array();
			foreach($structure as $field=>$format){
				if(empty($this->schema[$tablename])){
					$this->schema[$tablename]=array();
				}
				$this->schema[$tablename][$field]=$this->extractFieldInfos($this->tables[$tablename],$field,$format);
			}
		}
		foreach($this->tables as $table=>$attributes){
			if(!empty($attributes['foreignkey'])){
				foreach($attributes['foreignkey'] as $field=>$options){
					$this->schema[$table][$field] = array_merge($this->schema[$options['table']][$options['field']],$this->schema[$table][$field]);
				}
			}
		}
		$log .= print_r($this->tables,true);
		$log .= print_r($this->schema,true);
		__debug(__CLASS__."::serialize() ->  tables:".print_r($this->tables,true),__FILE__,__LINE__);
		__debug(__CLASS__."::serialize() ->  schema:".print_r($this->schema,true),__FILE__,__LINE__);
		return $log;
	}
	
	
	/**
	 * Call the database persistence converter to transform field model type into field db type.
	 * @param $type from model.ini.
	 * @return String Database corresponding field type.
	 */
	private function convert($type,$size,$options){
		return $this->data->getManager()->convertFieldType($type,$size,$options);
	}
	
	/**
	 * Generate queries based on schema array.
	 * @return unknown_type
	 */
	private function generateQueries(){
		$queries = array();
		foreach($this->schema as $table=>$structure){
			$query="CREATE TABLE $table (";
			foreach($structure as $field=>$infos){
				$options = (!empty($infos['options'])?$infos['options']:array());
				__debug(__CLASS__."::genereateQueries() - field=$field:[".print_r($infos,true)."]",__FILE__,__LINE__);
				$query.="$field ".$this->convert($infos['type'],$infos['size'],$options).",";
			}
			$query.=" PRIMARY KEY(".implode(",",$this->tables[$table]['primarykey']).")";			
			$query.=");";
			__debug(__CLASS__."::generateQueries() : query=$query",__FILE__,__LINE__);
			$log .= "<pre>$query</pre>";
		}
	}
	
	/**
	 * Create entities for each table from schema.
	 * @return log containing all created classes.
	 */
	private function createEntities(){
		global $framework;
		$log="";
		$classentity="";
		$endline="\n";
		foreach($this->schema as $name=>$attributes){
			$classentity   = "<?php$endline/**".$endline;
			$classentity  .= " * entity $name".$endline;
			$classentity  .= " *".$endline;
			$classentity  .= " *".$endline;
			$classentity  .= " */".$endline;
			$classentity  .= "require_once(\"$framework/entity.php\");".$endline;			
			$classentity  .= "class ".ucfirst($name)." extends Entity{".$endline;
			foreach($attributes as $attribute=>$infos){
				$classentity  .= "    public $$attribute;".$endline;
			}
			$classentity  .= "}$endline?>".$endline;
			$log .="<pre>$classentity</pre>";
			$fh=fopen($_SERVER['DOCUMENT_ROOT']."entities/$name.entity.php","w");
			if($fh){
				fwrite($fh,$classentity);
			}
			fclose($fh);
		}
		return $log;
	}
	
	/**
	 * Execute queries generated on the database.
	 * @return unknown_type
	 */
	private function execute(){
		
	}
	
	/**
	 * create all entity and persistence.
	 * @return log of all activities.
	 */	
	public function create(){
		$log = "<h1>Generation</h1>";
		$log .= $this->unserialize();
		$log = "<h2>Create PHP Class Entities</h2>";
		$log .= $this->createEntities();
		$log = "<h2>Generate SQL queries</h2>";
		$log .= $this->generateQueries();
		$log = "<h2>Execute to database</h2>";
		$log .= $this->execute();
		$log = "<h2>done.</h2>";
		__template_set("log",$log);

		return "model/model";
	}
	
	
	public function action($action=""){
		switch($action){
			case "generate":
				$status = $this->create();
				break;
			case "":
		}
		return $status;
	}
	
	
	/**
	 * return the corresponding instance.
	 * @return unknown_type
	 */
	public function getInstance(){
		if(empty(self::$_instance)){
			self::$_instance = new ModelManager();
		}
		return self::$_instance;
	}
}
?>